package abc.datasource;

import abc.utils.StringUtil;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.sql.SQLException;
import java.util.Map;

@Data
@Slf4j
@ConfigurationProperties(prefix = "spring.datasource.dynamic.druid")
public class DruidProperties {

    private Integer maxActive = 0; //连接池支持的最大连接数。一般取值20就可以了，一般把maxActive设置成可能的并发量就行了设 0 为没有限制。
    private Integer maxIdle; // 连接池中最多可空闲maxIdle个连接 ，这里取值为20，表示即使没有数据库连接时依然可以保持20空闲的连接，而不被清除，随时处于待命状态。设 0 为没有限制。已经不再使用，配置了也没效果
    private Integer minIdle; // 连接池中最小空闲连接数，当连接数少于此值时，连接池会创建连接来补充到该值的数量
    private Integer initialSize; // 初始化连接数目
    private Integer maxWait; // 连接池中连接用完时,新的请求等待时间,毫秒，这里取值-1，表示无限等待，直到超时为止，也可取值9000，表示9秒后超时。超过时间会出错误信息
    private Integer minEvictableIdleTimeMillis; // 连接池中连接可空闲的时间,单位为毫秒 针对连接池中的连接对象
    private Integer timeBetweenEvictionRunsMillis; // 每timeBetweenEvictionRunsMillis毫秒检查一次连接池中空闲的连接,把空闲时间超过minEvictableIdleTimeMillis毫秒的连接断开,直到连接池中的连接数到minIdle为止
    private Boolean useUnfairLock; // 是否启用非公平锁
    private Boolean testWhileIdle; // 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
    private Boolean testOnBorrow; // 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能
    private Boolean testOnReturn; // 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能
    private String validationQuery; // 在连接池返回连接给调用者前用来对连接进行验证的查询 SQL，要求为一条查询语句
    private Integer validationQueryTimeout; // SQL 查询验证超时时间（秒），小于或等于 0 的数值表示禁用
    private Boolean poolPreparedStatements; // 是否缓存preparedStatement，也就是PSCache。PSCache对支持游标的数据库性能提升巨大，比如说oracle。在mysql下建议关闭
    private Integer maxOpenPreparedStatements; // 要启用PSCache，必须配置大于0，当大于0时，poolPreparedStatements自动触发修改为true。在Druid中，不会存在Oracle下PSCache占用内存过多的问题，可以把这个数值配置大一些，比如说100
    private Boolean removeAbandoned; // 标记是否删除泄露的连接，如果连接超出removeAbandonedTimeout的限制，且该属性设置为 true，则连接被认为是被泄露并且可以被删除
    private Integer removeAbandonedTimeout; // 泄露的连接可以被删除的超时时间（秒），该值应设置为应用程序查询可能执行的最长时间
    private String connectionInitSqls; //物理连接初始化的时候执行的sql
    private String exceptionSorter; //当数据库抛出一些不可恢复的异常时，抛弃连接
    private String filters; //属性类型是字符串，通过别名的方式配置扩展插件，常用的插件有：监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall


    private Integer maxEvictableIdleTimeMillis;

    private Map<String, Map<String, String>> ds;

    public DruidDataSource dataSource(DruidDataSource datasource) {

        if(StringUtil.isNotEmpty(filters)) {
            try {
                datasource.setFilters(filters);
            } catch (SQLException e) {
                log.error(e.getMessage(), e);
            }
        }

        if(StringUtil.isNotEmpty(exceptionSorter)) {
            try {
                datasource.setExceptionSorter(exceptionSorter);
            } catch (SQLException e) {
                log.error(e.getMessage(), e);
            }
        }

        if(StringUtil.isNotEmpty(connectionInitSqls)) {
            datasource.setConnectionInitSqls(StringUtil.splitString2List(connectionInitSqls, ";"));
        }

        if(removeAbandonedTimeout != null) {
            datasource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
        }

        if(removeAbandoned != null) {
            datasource.setRemoveAbandoned(removeAbandoned);
        }

        if(maxOpenPreparedStatements != null) {
            datasource.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
        }

        if(poolPreparedStatements != null) {
            datasource.setPoolPreparedStatements(poolPreparedStatements);
        }

        if(maxIdle != null) {
            datasource.setMaxIdle(maxIdle);
        }

        /** 配置初始化大小、最小、最大 */
        if(initialSize != null) {
            datasource.setInitialSize(initialSize);
        }

        if(maxActive != null) {
            datasource.setMaxActive(maxActive);
        }

        if(minIdle != null) {
            datasource.setMinIdle(minIdle);
        }

        if(maxWait != null) {
            /** 配置获取连接等待超时的时间 */
            datasource.setMaxWait(maxWait);
        }

        if(timeBetweenEvictionRunsMillis != null) {
            /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        }

        /** 配置一个连接在池中最小、最大生存的时间，单位是毫秒 */
        if(minEvictableIdleTimeMillis != null) {
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        }

        if(minEvictableIdleTimeMillis != null) {
            datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
        }

        if(StringUtil.isNotEmpty(validationQuery)) {
            /**
             * 用来检测连接是否有效的sql，要求是一个查询语句，常用select 'x'。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
             */
            datasource.setValidationQuery(validationQuery);
        }

        if(testWhileIdle != null) {
            /** 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。 */
            datasource.setTestWhileIdle(testWhileIdle);
        }

        if(testOnBorrow != null) {
            /** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
            datasource.setTestOnBorrow(testOnBorrow);
        }

        if(testOnReturn != null) {
            /** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
            datasource.setTestOnReturn(testOnReturn);
        }

        if(removeAbandonedTimeout != null) {
            datasource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
        }

        if(useUnfairLock != null) {
            datasource.setUseUnfairLock(useUnfairLock);
        }

        if(validationQueryTimeout != null) {
            datasource.setValidationQueryTimeout(validationQueryTimeout);
        }

        return datasource;
    }
}
